// Copyright (c) 2019 Presto Labs Pte. Ltd.
// Author: jaewon

#pragma once

#include <cstdint>
#include <functional>
#include <string>
#include <vector>

#include <boost/functional/hash.hpp>

#include "coin/feed/fastfeed/feed.pb.h"

namespace fastfeed {
namespace view {

struct ViewKey {
  proto::FeedType feed_type;
  std::string custom_feed_type;
  std::string symbol;
  std::string view_type;

  bool operator==(const ViewKey& other) const {
    return (
        feed_type == other.feed_type && custom_feed_type == other.custom_feed_type &&
        symbol == other.symbol && view_type == other.view_type);
  }

  std::size_t hash_value() const {
    std::size_t value = 0;
    boost::hash_combine(value, feed_type);
    boost::hash_combine(value, custom_feed_type);
    boost::hash_combine(value, symbol);
    boost::hash_combine(value, view_type);
    return value;
  }
};  // struct ViewKey

inline std::size_t hash_value(const ViewKey& key) { return key.hash_value(); }

class ViewCollection;

class ViewBase {
 public:
  virtual ~ViewBase() = default;

  proto::FeedType feed_type() const { return view_key_.feed_type; }
  const std::string& custom_feed_type() const { return view_key_.custom_feed_type; }
  const std::string& symbol() const { return view_key_.symbol; }
  const std::string& view_type() const { return view_key_.view_type; }

  virtual void Init(ViewCollection* view_collection) = 0;

  // Internal methods
  void SetViewKey(const ViewKey& view_key);
  void Subscribe(std::function<void(const ViewBase*)> callback);

 protected:
  void InvokeCallbacks();

 private:
  ViewKey view_key_;
  std::vector<std::function<void(const ViewBase*)>> callbacks_;
};  // class ViewBase

class BuilderBase : public ViewBase {
 public:
  virtual ~BuilderBase() = default;

  int64_t timestamp() const { return timestamp_; }
  int64_t origin_timestamp() const { return origin_timestamp_; }

  // Internal methods.
  void Init(ViewCollection* view_collection) override;

  // User implementation.
  virtual void InitBuilder() {}

  // Returns true on a notifiable event.
  virtual bool UpdateByProto(const proto::Feed& feed) = 0;

 protected:
  proto::DataType data_type() const { return data_type_; }

  inline void UpdateHeader(const proto::Feed& feed) {
    data_type_ = feed.data_type();
    timestamp_ = feed.timestamp();
    origin_timestamp_ = feed.origin_timestamp();
  }

  void OnFeed(const proto::Feed& feed);

 private:
  proto::DataType data_type_ = proto::DATA_TYPE_INVALID;
  int64_t timestamp_ = 0;
  int64_t origin_timestamp_ = 0;
};  // class BuilderBase

}  // namespace view
}  // namespace fastfeed
